/* * @(#)JComponent.java 2.61 98/04/10 * * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved. * * This software is the confidential and proprietary information of Sun * Microsystems, Inc. ("Confidential Information"). You shall not * disclose such Confidential Information and shall use it only in * accordance with the terms of the license agreement you entered into * with Sun. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING * THIS SOFTWARE OR ITS DERIVATIVES. * */ package com.sun.java.swing; import java.awt.Component; import java.awt.Container; import java.awt.Color; import java.awt.Cursor; import java.awt.Window; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Dimension; import java.awt.Insets; import java.awt.Graphics; import java.awt.Window; import java.awt.AWTEvent; import java.awt.Rectangle; import java.util.Hashtable; import java.util.Dictionary; import java.util.Enumeration; import java.util.Locale; import java.util.Vector; import java.awt.Point; import java.awt.Image; import java.awt.LayoutManager2; import java.awt.Color; import java.awt.event.*; import java.beans.*; import java.applet.Applet; import java.io.Serializable; import java.io.ObjectOutputStream; import java.io.ObjectInputStream; import java.io.IOException; import com.sun.java.swing.border.*; import com.sun.java.swing.event.*; import com.sun.java.swing.plaf.*; import com.sun.java.accessibility.*; /** * The base class for the Swing components. JComponent provides: *
* Warning: serialized objects of this class will not be compatible with * future swing releases. The current serialization support is appropriate * for short term storage or RMI between Swing1.0 applications. It will * not be possible to load serialized Swing1.0 objects with future releases * of Swing. The JDK1.2 release of Swing will be the compatibility * baseline for the serialized form of Swing objects. * * @see KeyStroke * @see Action * @see #setBorder * @see #registerKeyboardAction * @see JOptionPane * @see #setDebugGraphicsOptions * @see #setToolTipText * @see #setAutoscrolls * * @version 2.61 04/10/98 * @author Hans Muller * @author Arnaud Weber */ public abstract class JComponent extends Container implements Serializable { /* The following fields support set methods for the corresponding * java.awt.Component properties. */ private Dimension preferredSize; private Dimension minimumSize; private Dimension maximumSize; private Float alignmentX; private Float alignmentY; private AncestorNotifier ancestorNotifier; Rectangle _bounds = new Rectangle(); /* Backing store for JComponent properties and listeners */ protected ComponentUI ui; protected EventListenerList listenerList = new EventListenerList(); private Hashtable clientProperties; private VetoableChangeSupport vetoableChangeSupport; private Autoscroller autoscroller; private int flags; private Border border; private transient Rectangle tmpRect; /** * Constant used for registerKeyboardAction() which * means that the command should be invoked when * the component has the focus. */ public static final int WHEN_FOCUSED = 0; /** * Constant used for registerKeyboardAction() which * means that the comand should be invoked when the receiving * component is an ancestor of the focused component or is * itself the focused component. */ public static final int WHEN_ANCESTOR_OF_FOCUSED_COMPONENT = 1; /** * Constant used for registerKeyboardAction() which * means that the command should be invoked when * the receiving component is in the window that has the focus * or is itself the focused component. */ public static final int WHEN_IN_FOCUSED_WINDOW = 2; /** * Constant used by some of the apis to mean that no condition is defined. */ public static final int UNDEFINED_CONDITION = -1; /** * The key used by JComponent to access keyboard bindings. */ private static final String KEYBOARD_BINDINGS_KEY = "_KeyboardBindings"; /** * The comment to display when the cursor is over the component, * also known as a "value tip", "flyover help", or "flyover label". */ public static final String TOOL_TIP_TEXT_KEY = "ToolTipText"; private static final String NEXT_FOCUS = "nextFocus"; /** Private flags **/ private static final int REQUEST_FOCUS_DISABLED = 0; private static final int IS_DOUBLE_BUFFERED = 1; private static final int ANCESTOR_USING_BUFFER = 2; private static final int IS_PAINTING_TILE = 3; private static final int HAS_FOCUS = 4; private static final int IS_OPAQUE = 5; /** * Default JComponent constructor. This constructor does * no initialization beyond calling the Container constructor, * e.g. the initial layout manager is null. */ public JComponent() { super(); enableEvents(AWTEvent.FOCUS_EVENT_MASK); } /** * Resets the UI property to a value from the current look and feel. * JComponent subclasses must override this method like this: *
* public void updateUI() { * setUI((SliderUI)UIManager.getUI(this); * } ** * @see #setUI * @see UIManager#getLookAndFeel * @see UIManager#getUI */ public void updateUI() {} /** * Set the look and feel delegate for this component. * JComponent subclasses generally override this method * to narrow the argument type, e.g. in JSlider: *
* public void setUI(SliderUI newUI) { * super.setUI(newUI); * } **
* Additionaly JComponent subclasses must provide a getUI * method that returns the correct type, e.g. *
* public SliderUI getUI() { * return (SliderUI)ui; * } ** * @see #updateUI * @see UIManager#getLookAndFeel * @see UIManager#getUI */ protected void setUI(ComponentUI newUI) { /* We do not check that the UI instance is different * before allowing the switch in order to enable the * same UI instance *with different default settings* * to be installed. */ if (ui != null) { ui.uninstallUI(this); } ComponentUI oldUI = ui; ui = newUI; if (ui != null) { ui.installUI(this); } invalidate(); firePropertyChange("UI", oldUI, ui); } /** * Return the UIDefaults key used to look up the name of the * swing.plaf.ComponentUI class that defines the look and feel * for this component. Most applications will never need to * call this method. Subclasses of JComponent that support * pluggable look and feel should override this method to * return the name of the ComponentUI subclass that defines * their look and feel. * * @return The name of a ComponentUI subclass. * @see UIDefaults#getUI * @beaninfo * expert: true * description: UIClassID */ public String getUIClassID() { return "not a pluggable look and feel class"; } /** * Returns the graphics object used to paint this component. * If DebugGraphics is turned on we create a new DebugGraphics * object if neccessary otherwise we just configure the * specified graphics objects foreground and font. * * @return A Graphics object configured for this component */ protected Graphics getComponentGraphics(Graphics g) { Graphics componentGraphics = g; if (ui != null) { if ((DebugGraphics.debugComponentCount() != 0) && (shouldDebugGraphics() != 0) && !(g instanceof DebugGraphics)) { if(g instanceof SwingGraphics) { if(!(((SwingGraphics)g).subGraphics() instanceof DebugGraphics)) { componentGraphics = new DebugGraphics(((SwingGraphics)g).subGraphics(),this); componentGraphics = SwingGraphics.createSwingGraphics(componentGraphics); } } else { componentGraphics = new DebugGraphics(g,this); } } } componentGraphics.setColor(getForeground()); componentGraphics.setFont(getFont()); return componentGraphics; } /** * If the UI delegate is non-null, call its paint * method. We pass the delegate a copy of the Graphics * object to protect the rest of the paint code from * irrevocable changes (e.g. Graphics.translate()). * * @see #paint */ protected void paintComponent(Graphics g) { if (ui != null) { Graphics scratchGraphics = SwingGraphics.createSwingGraphics(g.create()); try { ui.update(scratchGraphics, this); } finally { scratchGraphics.dispose(); } } } /** * Paint this component's children. * If shouldUseBuffer is true, no component ancestor has a buffer and * the component children can use a buffer if they have one. * Otherwise, one ancestor has a buffer currently in use and children * should not use a buffer to paint. * @see #paint * @see java.awt.Container#paint */ protected void paintChildren(Graphics g) { boolean isJComponent; boolean shouldRecycle = false; SwingGraphics sg; if(g instanceof SwingGraphics) { sg = (SwingGraphics)g; } else { sg = SwingGraphics.createSwingGraphics(g); shouldRecycle = true; } synchronized(getTreeLock()) { for (int i = getComponentCount() - 1 ; i >= 0 ; i--) { Component comp = getComponent(i); if (comp != null && isLightweightComponent(comp) && (comp.isVisible() == true)) { Rectangle cr; isJComponent = (comp instanceof JComponent); if(isJComponent) { if(tmpRect == null) { tmpRect = new Rectangle(); } cr = tmpRect; ((JComponent)comp).getBounds(cr); } else { cr = comp.getBounds(); } if (sg.isClipIntersecting(cr)) { Graphics cg = SwingGraphics.createGraphics( g, cr.x, cr.y, cr.width, cr.height); boolean shouldSetFlagBack = false; try { if(isJComponent) { if(getFlag(ANCESTOR_USING_BUFFER)) { ((JComponent)comp).setFlag(ANCESTOR_USING_BUFFER,true); shouldSetFlagBack = true; } if(getFlag(IS_PAINTING_TILE)) { ((JComponent)comp).setFlag(IS_PAINTING_TILE,true); shouldSetFlagBack = true; } ((JComponent)comp).paint(cg); } else { comp.paint(cg); } } finally { cg.dispose(); if(shouldSetFlagBack) { ((JComponent)comp).setFlag(ANCESTOR_USING_BUFFER,false); ((JComponent)comp).setFlag(IS_PAINTING_TILE,false); } } } } } } if(shouldRecycle) { sg.recycle(); } } /** * Paint the component's border. * * @see #paint * @see #setBorder */ protected void paintBorder(Graphics g) { Border border = getBorder(); if (border != null) { border.paintBorder(this, g, 0, 0, getWidth(), getHeight()); } } /** * Calls paint(g). Doesn't clear the background but see * ComponentUI.update() which is called by paintComponent. * * @see #paint * @see #paintComponent * @see com.sun.java.swing.plaf.ComponentUI */ public void update(Graphics g) { paint(g); } /** * This method is invoked by Swing to draw components. * Applications should not invoke paint directly, * but should instead use the
repaint
method to
* schedule the component for redrawing.
*
* This method actually delegates the work of painting to three
* protected methods: paintComponent
, paintBorder
,
* and paintChildren
. They're called in the order
* listed to ensure that children appear on top of component itself.
* Generally speaking, the component and its children should not
* paint in the insets area allocated to the border. Subclasses can
* just override this method, as always. A subclass that just
* wants to specialize the UI (look and feel) delegates paint
* method should just override paintComponent
.
*
* @see #paintComponent
* @see #paintBorder
* @see #paintChildren
* @see #getComponentGraphics
* @see #repaint
*/
public void paint(Graphics g) {
boolean shouldClearPaintFlags = false;
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Graphics componentGraphics = getComponentGraphics(g);
boolean shouldRecycle;
if((componentGraphics instanceof SwingGraphics)) {
shouldRecycle = false;
} else {
shouldRecycle = true;
}
SwingGraphics co = SwingGraphics.createSwingGraphics(componentGraphics);
Image offscr = null;
RepaintManager repaintManager = RepaintManager.currentManager(this);
int clipX = co.getClipX();
int clipY = co.getClipY();
int clipW = co.getClipWidth();
int clipH = co.getClipHeight();
if(clipW > getWidth()) {
clipW = getWidth();
}
if(clipH > getHeight()) {
clipH = getHeight();
}
if(getParent() != null && !(getParent() instanceof JComponent)) {
adjustPaintFlags();
shouldClearPaintFlags = true;
}
if(repaintManager.isDoubleBufferingEnabled() &&
!getFlag(ANCESTOR_USING_BUFFER) && isDoubleBuffered()) {
int bw,bh;
int x,y,maxx,maxy;
offscr = repaintManager.getOffscreenBuffer(this,clipW,clipH);
if(shouldRecycle) {
co.recycle();
shouldRecycle = false;
}
co = SwingGraphics.createSwingGraphics(offscr.getGraphics());
co.translate(-clipX,-clipY);
bw = offscr.getWidth(null);
bh = offscr.getHeight(null);
if(bw > clipW) {
bw = clipW;
}
if(bh > clipH) {
bh = clipH;
}
try {
setFlag(ANCESTOR_USING_BUFFER,true);
setFlag(IS_PAINTING_TILE,true);
for(x = 0, maxx = clipW; x < maxx ; x += bw ) {
for(y=0, maxy = clipH; y < maxy ; y += bh) {
if((y+bh) >= maxy && (x+bw) >= maxx)
setFlag(IS_PAINTING_TILE,false);
co.translate(-x,-y);
co.setClip(clipX+x,clipY + y,bw,bh);
if(!rectangleIsObscured(clipX,clipY,bw,bh)) {
paintComponent(co);
paintBorder(co);
}
paintChildren(co);
g.drawImage(offscr,clipX + x,clipY + y,this);
co.translate(x,y);
}
}
} finally {
setFlag(ANCESTOR_USING_BUFFER,false);
setFlag(IS_PAINTING_TILE,false);
co.dispose();
}
} else {
if (!rectangleIsObscured(clipX,clipY,clipW,clipH)) {
paintComponent(co);
paintBorder(co);
}
paintChildren(co);
}
if(shouldRecycle) {
co.recycle();
}
if(shouldClearPaintFlags) {
setFlag(ANCESTOR_USING_BUFFER,false);
setFlag(IS_PAINTING_TILE,false);
}
}
private void adjustPaintFlags() {
JComponent jparent = null;
Container parent;
for(parent = getParent() ; parent != null ; parent =
parent.getParent()) {
if(parent instanceof JComponent) {
jparent = (JComponent) parent;
if(jparent.getFlag(ANCESTOR_USING_BUFFER))
setFlag(ANCESTOR_USING_BUFFER, true);
if(jparent.getFlag(IS_PAINTING_TILE))
setFlag(IS_PAINTING_TILE, true);
break;
}
}
}
/**
* Returns true if the receiving component is currently painting a tile.
* If this method returns true, paint will be called again for another
* tile. This method returns false if you are not painting a tile or
* if the last tile is painted.
* Use this method to keep some state you might need between tiles.
*/
public boolean isPaintingTile() {
return getFlag(IS_PAINTING_TILE);
}
/**
* Override this method and return true if your component is the root of
* of a component tree with its own focus cycle.
*/
public boolean isFocusCycleRoot() {
return false;
}
/**
* Override this method and return true if your JComponent manages focus.
* If your component manages focus, the focus manager will handle your
* component's children. All key event will be sent to your key listener
* including TAB and SHIFT+TAB. CONTROL + TAB and CONTROL + SHIFT + TAB
* will move the focus to the next / previous component.
*/
public boolean isManagingFocus() {
return false;
}
/**
* Specifies the next component to get the focus after this one,
* for example, when the tab key is pressed. Invoke this method
* to override the default focus-change sequence.
* @beaninfo
* expert: true
* description: The next component to get focus after this one.
*/
public void setNextFocusableComponent(Component aComponent) {
putClientProperty(NEXT_FOCUS,aComponent);
}
/**
* Return the next focusable component or null if the focus manager
* should choose the next focusable component automatically
*/
public Component getNextFocusableComponent() {
return (Component) getClientProperty(NEXT_FOCUS);
}
/**
* Set whether the receiving component can obtain the focus by
* calling requestFocus. The default value is true.
* Note: Setting this property to false will not prevent the focus
* manager from setting the focus to this component, it will prevent
* the component from getting the focus when the focus is requested
* explicitly. Override isFocusTraversable and return false if the
* component should never get the focus.
* @beaninfo
* expert: true
* description: Whether the component can obtain the focus by calling requestFocus.
*/
public void setRequestFocusEnabled(boolean aFlag) {
setFlag(REQUEST_FOCUS_DISABLED,(aFlag ? false:true));
}
/** Return whether the receiving component can obtain the focus by
* calling requestFocus
* @see #setRequestFocusEnabled()
*/
public boolean isRequestFocusEnabled() {
return (getFlag(REQUEST_FOCUS_DISABLED) ? false : true);
}
/** Set focus on the receiving component if isRequestFocusEnabled returns true **/
public void requestFocus() {
if(isRequestFocusEnabled()) {
super.requestFocus();
}
}
/** Set the focus on the receiving component. This method is for focus managers, you
* rarely want to call this method, use requestFocus() enstead.
*/
public void grabFocus() {
super.requestFocus();
}
/**
* Set the preferred size of the receiving component.
* if preferredSize
is null, the UI will
* be asked for the preferred size
* @beaninfo
* preferred: true
* description: The preferred size of the component.
*/
public void setPreferredSize(Dimension preferredSize) {
this.preferredSize = preferredSize;
}
/**
* If the preferredSize has been set to a non-null value
* just return it. If the UI delegates getPreferredSize()
* method returns a non null then value return that, otherwise
* defer to the components layout manager.
*
* @return the value of the preferredSize property.
* @see #setPreferredSize
*/
public Dimension getPreferredSize() {
if (preferredSize != null) {
return preferredSize;
}
Dimension size = null;
if (ui != null) {
size = ui.getPreferredSize(this);
}
return (size != null) ? size : super.getPreferredSize();
}
/**
* Sets the maximumSize of this component to a constant
* value. Subsequent calls to getMaximumSize will always
* return this value, the components UI will not be asked
* to compute it. Setting the maximumSize to null
* restores the default behavior.
*
* @see #getMaximumSize
* @beaninfo
* preferred: true
* description: The maximum size of the component.
*/
public void setMaximumSize(Dimension maximumSize) {
this.maximumSize = maximumSize;
}
/**
* If the maximumSize has been set to a non-null value
* just return it. If the UI delegates getMaximumSize()
* method returns a non null value then return that, otherwise
* defer to the components layout manager.
*
* @return the value of the maximumSize property.
* @see #setMaximumSize
*/
public Dimension getMaximumSize() {
if (maximumSize != null) {
return maximumSize;
}
Dimension size = null;
if (ui != null) {
size = ui.getMaximumSize(this);
}
return (size != null) ? size : super.getMaximumSize();
}
/**
* Sets the minimumSize of this component to a constant
* value. Subsequent calls to getMinimumSize will always
* return this value, the components UI will not be asked
* to compute it. Setting the minimumSize to null
* restores the default behavior.
*
* @see #getMinimumSize
* @beaninfo
* preferred: true
* description: The minimum size of the component.
*/
public void setMinimumSize(Dimension minimumSize) {
this.minimumSize = minimumSize;
}
/**
* If the minimumSize has been set to a non-null value
* just return it. If the UI delegates getMinimumSize()
* method returns a non null value then return that, otherwise
* defer to the components layout manager.
*
* @return the value of the minimumSize property.
* @see #setMinimumSize
*/
public Dimension getMinimumSize() {
if (minimumSize != null) {
return minimumSize;
}
Dimension size = null;
if (ui != null) {
size = ui.getMinimumSize(this);
}
return (size != null) ? size : super.getMinimumSize();
}
/**
* Give the UI delegate an opportunity to define the precise
* shape of this component for the sake of mouse processing.
*
* @return true if this component logically contains x,y.
* @see java.awt.Component#contains(int, int)
*/
public boolean contains(int x, int y) {
return (ui != null) ? ui.contains(this, x, y) : super.contains(x, y);
}
/**
* Sets the border of this component. The Border object is
* responsible for defining the insets for the component
* (overriding any insets set directly on the component) and
* for optionally rendering any border decorations within the
* bounds of those insets. Borders should be used (rather
* than insets) for creating both decorative and non-decorative
* (e.g. margins and padding) regions for a swing component.
* Compound borders can be used to nest multiple borders within a
* single component.
*
* This is a bound property. * * @param border the border to be rendered for this component * @see Border * @see CompoundBorder * @beaninfo * bound: true * preferred: true * description: The component's border. */ public void setBorder(Border border) { Border oldBorder = this.border; this.border = border; firePropertyChange("border", oldBorder, border); invalidate(); } /** * Returns the border of this component or null if no border is * currently set. * * @return the border object for this component * @see setBorder */ public Border getBorder() { return border; } /** * If a border has been set on this component, returns the * border's insets, else calls super.getInsets. * * @return the value of the insets property. * @see #setBorder */ public Insets getInsets() { if (border != null) { return border.getBorderInsets(this); } return super.getInsets(); } /** * @return the value of the alignmentY property. * @see #setAlignmentY * @see java.awt.Component#getAlignmentY */ public float getAlignmentY() { return (alignmentY != null) ? alignmentY.floatValue() : super.getAlignmentY(); } /** * Set the value of the alignmentY property. * @see #getAlignmentY * @beaninfo * preferred: true * description: The preferred vertical alignment of the component */ public void setAlignmentY(float alignmentY) { this.alignmentY = new Float(alignmentY > 1.0f ? 1.0f : alignmentY < 0.0f ? 0.0f : alignmentY); } /** * @return the value of the alignmentX property. * @see #setAlignmentX * @see java.awt.Component#getAlignmentX */ public float getAlignmentX() { return (alignmentX != null) ? alignmentX.floatValue() : super.getAlignmentX(); } /** * Set the value of the alignmentX property. * @see #getAlignmentX * @beaninfo * preferred: true * description: The preferred horizontal alignment of the component */ public void setAlignmentX(float alignmentX) { this.alignmentX = new Float(alignmentX > 1.0f ? 1.0f : alignmentX < 0.0f ? 0.0f : alignmentX); } /** * Returns this component's graphics context, which lets you draw * on a component. Use this method get a Graphics object and * then invoke oeprations on that object to draw on the component. */ public Graphics getGraphics() { if (shouldDebugGraphics() != 0) { DebugGraphics graphics = new DebugGraphics(super.getGraphics(), this); return graphics; } return SwingGraphics.createSwingGraphics(super.getGraphics()); } /** Enables or disables diagnostic information about every graphics * operation performed within the component or one of its children. The * value of debugOptions determines how the component should * display this information: *
* The aCommand will be set in the delivered event if specified. *
* The Condition can be one of: *
***
*- WHEN_FOCUSED *
- The action will be invoked only when the keystroke occurs * while the component has the focus. *
- WHEN_IN_FOCUSED_WINDOW *
- The action will be invoked when the keystroke occurs while * the component has the focus or if the component is in the * window that has the focus. Note that the component need not * be an immediate descendent of the window -- it can be * anywhere in the window's containment hierarchy. In other * words, whenever any component in the window has the focus, * the action registered with this component is invoked. *
- WHEN_ANCESTOR_OF_FOCUSED_COMPONENT *
- The action will be invoked when the keystroke occurs while the * component has the focus or if the component is an ancestor of * the component that has the focus. *
* The combination of keystrokes and conditions lets you define high * level (semantic) action events for a specified keystroke+modifier * combination (using the KeyStroke class) and direct to a parent or * child of a component that has the focus, or to the component itself. * In other words, in any hierarchical structure of components, an * arbitrary key-combination can be immediately directed to the * appropriate component in the hierarchy, and cause a specific method * to be invoked (usually by way of adapter objects). *
* If an action has already been registered for the receiving * container, with the same charCode and the same modifiers, * anAction will replace the action. * * @see KeyStroke */ public void registerKeyboardAction(ActionListener anAction,String aCommand,KeyStroke aKeyStroke,int aCondition) { Hashtable bindings; boolean firstKeyboardAction = false; synchronized(this) { bindings = (Hashtable) getClientProperty(KEYBOARD_BINDINGS_KEY); if(bindings == null) { bindings = new Hashtable(); putClientProperty(KEYBOARD_BINDINGS_KEY,bindings); firstKeyboardAction = true; } } synchronized(bindings) { bindings.put(aKeyStroke,new KeyboardBinding(anAction,aCommand,aKeyStroke,aCondition)); } /* This is the first time a keyboard binding is added, let's order * keyboard events... * ALERT: we need to enable events. Adding a listener will not work since * we want our listener to be after all other listeners. */ if(firstKeyboardAction) { enableEvents(AWTEvent.KEY_EVENT_MASK); } } /** * Calls registerKeyboardAction(ActionListener,String,KeyStroke,condition) with a null command. */ public void registerKeyboardAction(ActionListener anAction,KeyStroke aKeyStroke,int aCondition) { registerKeyboardAction(anAction,null,aKeyStroke,aCondition); } private Hashtable keyboardBindings() { Hashtable bindings; synchronized(this) { bindings = (Hashtable) getClientProperty(KEYBOARD_BINDINGS_KEY); } return bindings; } /** * Unregister a keyboard action. * * @see #registerKeyboardAction */ public void unregisterKeyboardAction(KeyStroke aKeyStroke) { Hashtable bindings = keyboardBindings(); if(bindings == null) return; synchronized(bindings) { bindings.remove(aKeyStroke); } if(bindings.size() == 0) { /** ALERT. We need a way to disable keyboard events only if there is no * keyboard listener. */ } } /** * Return the KeyStrokes that will initiate registered actions. * * @return an array of KeyStroke objects * @see #registerKeyboardAction */ public KeyStroke[] getRegisteredKeyStrokes() { Hashtable bindings = keyboardBindings(); KeyStroke result[]; int i; Enumeration keys; if(bindings == null) return new KeyStroke[0]; synchronized(bindings) { result = new KeyStroke[bindings.size()]; i = 0; keys = bindings.keys(); while(keys.hasMoreElements()) result[i++] = (KeyStroke) keys.nextElement(); } return result; } /** * Return the condition that determines whether a registered action * occurs in response to the specified keystroke. * * @return the action-keystroke condition * @see #registerKeyboardAction */ public int getConditionForKeyStroke(KeyStroke aKeyStroke) { Hashtable bindings = keyboardBindings(); if(bindings == null) return UNDEFINED_CONDITION; synchronized(bindings) { KeyboardBinding kb = (KeyboardBinding) bindings.get(aKeyStroke); if(kb != null) { return kb.getCondition(); } } return UNDEFINED_CONDITION; } /** * Return the object that will perform the action registered for a * given keystroke. * * @return the ActionListener object invoked when the keystroke occurs * @see #registerKeyboardAction */ public ActionListener getActionForKeyStroke(KeyStroke aKeyStroke) { Hashtable bindings = keyboardBindings(); if(bindings == null) return null; synchronized(bindings) { KeyboardBinding kb = (KeyboardBinding) bindings.get(aKeyStroke); if(kb != null) { return kb.getAction(); } } return null; } /** * Unregister all keyboard actions * * @see #registerKeyboardAction */ public void resetKeyboardActions() { synchronized(this) { Hashtable bindings = (Hashtable) getClientProperty(KEYBOARD_BINDINGS_KEY); if(bindings != null) { bindings.clear(); } } /* ALERT. We need a way to disable keyboard events only if there is no * keyboard listener. */ } /** * Request the focus for the component that should have the focus * by default. The default implementation will recursively request * the focus on the first component that is focus-traversable. * * @return false if the focus has not been set, otherwise * return true */ public boolean requestDefaultFocus() { Component ca[] = getComponents(); int i; for(i=0 ; i < ca.length ; i++) { if(ca[i].isFocusTraversable()) { if(ca[i] instanceof JComponent) { ((JComponent)ca[i]).grabFocus(); } else { ca[i].requestFocus(); } return true; } if(ca[i] instanceof JComponent && !((JComponent)ca[i]).isManagingFocus()) { if(((JComponent)(ca[i])).requestDefaultFocus()) { return true; } } } return false; } public void setVisible(boolean aFlag) { if(aFlag != isVisible()) { super.setVisible(aFlag); Container parent = getParent(); if(parent != null) { Rectangle r = getBounds(); parent.repaint(r.x,r.y,r.width,r.height); } } } /** * Identifies whether or not this component can receive the focus. * A disabled button, for example, would return false. * * @return true if this component can receive the focus */ public boolean isFocusTraversable() { boolean result = false; Hashtable bindings; synchronized(this) { bindings = (Hashtable) getClientProperty(KEYBOARD_BINDINGS_KEY); } if(bindings != null) { synchronized(bindings) { Enumeration keys = bindings.keys(); KeyboardBinding b; while(keys.hasMoreElements()) { b = (KeyboardBinding) bindings.get(keys.nextElement()); if(b.getCondition() == WHEN_FOCUSED) { result = true; break; } } } } return result; } protected void processFocusEvent(FocusEvent e) { switch(e.getID()) { case FocusEvent.FOCUS_GAINED: setFlag(HAS_FOCUS, true); break; case FocusEvent.FOCUS_LOST: setFlag(HAS_FOCUS, false); break; } // Call super *after* setting flag, in case listener calls paint. super.processFocusEvent(e); } /** * Process any key events that the component itself * recognizes. This will be called after the focus * manager and any interested listeners have been * given a chance to steal away the event. This * method will only be called is the event has not * yet been consumed. This method is called prior * to the keyboard UI logic. *
* This is implemented to do nothing. Subclasses would * normally override this method if they process some * key events themselves. If the event is processed, * it should be consumed. */ protected void processComponentKeyEvent(KeyEvent e) { } /** Override processKeyEvent to process events **/ protected void processKeyEvent(KeyEvent e) { // focus manager gets to steal the event if it wants it. boolean result; boolean shouldProcessKey = false; if(FocusManager.isFocusManagerEnabled()) { FocusManager focusManager = FocusManager.getCurrentManager(); focusManager.processKeyEvent(this,e); if(e.isConsumed()) { return; } } // This gives the key event listeners a crack at the event super.processKeyEvent(e); // give the component itself a crack at the event if (! e.isConsumed()) { processComponentKeyEvent(e); } if(e.getID() == KeyEvent.KEY_PRESSED) { shouldProcessKey = true; if(!KeyboardState.keyIsPressed(e.getKeyCode())) KeyboardState.registerKeyPressed(e.getKeyCode()); } else if(e.getID() == KeyEvent.KEY_RELEASED) { if(KeyboardState.keyIsPressed(e.getKeyCode())) { shouldProcessKey = true; KeyboardState.registerKeyReleased(e.getKeyCode()); } } else if(e.getID() == KeyEvent.KEY_TYPED) { shouldProcessKey = true; } if(e.isConsumed()) { return; } if(shouldProcessKey && e.getID() == KeyEvent.KEY_PRESSED) { result = processKeyBindings(e,true); if(result) e.consume(); } else if(shouldProcessKey && e.getID() == KeyEvent.KEY_RELEASED) { result = processKeyBindings(e,false); if(result) { e.consume(); } } else if(shouldProcessKey && e.getID() == KeyEvent.KEY_TYPED) { result = processKeyBindings(e,false); if(result) { e.consume(); } } } KeyboardBinding bindingForKeyStroke(KeyStroke ks,int condition) { Hashtable bindings; KeyboardBinding b; KeyboardBinding result = null; // synchronized(this) { bindings = (Hashtable) getClientProperty(KEYBOARD_BINDINGS_KEY); // } if(bindings != null) { // synchronized(bindings) { b = (KeyboardBinding) bindings.get(ks); // System.out.println("Bindings are " + bindings); if(b != null) { ActionListener action = b.getAction(); if((action instanceof Action) && !(((Action)action).isEnabled())) action = null; if(action != null) { switch(b.getCondition()) { case WHEN_FOCUSED: if(condition == WHEN_FOCUSED) result = b; break; case WHEN_ANCESTOR_OF_FOCUSED_COMPONENT: if(condition == WHEN_FOCUSED || condition == WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) result = b; break; case WHEN_IN_FOCUSED_WINDOW: if(condition == WHEN_FOCUSED || condition == WHEN_IN_FOCUSED_WINDOW || condition == WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) result = b; break; } } } // } } return result; } boolean processKeyBinding(KeyEvent e,int condition,boolean pressed) { Hashtable bindings; int i,c; boolean onKeyRelease = (pressed?false:true); KeyboardBinding binding = null; KeyStroke ks; if(isEnabled()) { if(e.getID() == KeyEvent.KEY_TYPED) { binding = bindingForKeyStroke((ks=KeyStroke.getKeyStroke(e.getKeyChar())),condition); } else { binding = bindingForKeyStroke((ks=KeyStroke.getKeyStroke(e.getKeyCode(),e.getModifiers(), onKeyRelease)), condition); } //System.out.println("e=" + e + "ks is " + ks); if(binding != null) { ActionListener listener = binding.getAction(); if(listener != null) { listener.actionPerformed(new ActionEvent(this,ActionEvent.ACTION_PERFORMED,binding.getCommand())); return true; } } } return false; } boolean processKeyBindings(KeyEvent e,boolean pressed) { Container parent; Vector processedComponents = new Vector(); /* Do we have a key binding for e? */ if(processKeyBinding(e,WHEN_FOCUSED,pressed)) return true; processedComponents.addElement(this); /* We have no key binding. Let's try the path from our parent to the window excluded * We store the path components so we can avoid asking the same component twice. */ parent = this.getParent(); while(parent != null && !(parent instanceof Window) && !(parent instanceof Applet)) { if(parent instanceof JComponent) { if(((JComponent)parent).processKeyBinding(e,WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,pressed)) return true; processedComponents.addElement(parent); } parent = parent.getParent(); } /* No components between the focused component and the window is actually interested * by the key event. Let's try the other JComponent in this window. */ if(parent != null) { return JComponent.processKeyBindingsForAllComponents(e,parent,processedComponents,pressed); } return false; } static boolean processKeyBindingsForAllComponents(KeyEvent e,Container container,Vector alreadyProcessed, boolean pressed) { int i; Component subComponents[]; if(!container.isVisible() || !container.isEnabled()) { return false; } if(container instanceof JComponent && !alreadyProcessed.contains(container)) { if(((JComponent)container).processKeyBinding(e,WHEN_IN_FOCUSED_WINDOW,pressed)) return true; } subComponents = container.getComponents(); for(i=0 ; i < subComponents.length ; i++) { if(subComponents[i].isVisible() && subComponents[i].isEnabled()) { if(subComponents[i] instanceof Container) { if(processKeyBindingsForAllComponents(e,(Container)subComponents[i],alreadyProcessed,pressed)) return true; } } } return false; } /** * Registers the text to display in a tool tip. * The text displays when the cursor lingers over the component. *
* See How to Use Tool Tips
* in The Java Tutorial
* for further documentation.
*
* @param text The string to display. If the text is null,
* the tool tip is turned off for this component.
* @see #TOOL_TIP_TEXT_KEY
* @beaninfo
* preferred: true
* description: The text to display in a tool tip.
*/
public void setToolTipText(String text) {
putClientProperty(TOOL_TIP_TEXT_KEY, text);
ToolTipManager toolTipManager = ToolTipManager.sharedInstance();
if (text != null) {
toolTipManager.registerComponent(this);
} else {
toolTipManager.unregisterComponent(this);
}
}
/**
* Return the tooltip string that has been set with setToolTipText()
*
* @return the text of the tool tip
* @see #TOOL_TIP_TEXT_KEY
*/
public String getToolTipText() {
return (String)getClientProperty(TOOL_TIP_TEXT_KEY);
}
/**
* Returns the string to be used as the tooltip for event. By default
* this returns any string set using setToolTipText(). If a component provides
* more extensize API to support differing tooltips at different locations,
* this method should be overridden.
*/
public String getToolTipText(MouseEvent event) {
return getToolTipText();
}
/**
* Return the tooltip location in the receiving component coordinate system
* If null is returned, Swing will choose a location.
* The default implementation returns null.
*
* @param event the MouseEvent that caused the ToolTipManager to
* show the tooltip.
*/
public Point getToolTipLocation(MouseEvent event) {
return null;
}
/**
* Returns the instance of JToolTip that should be used to display the tooltip.
* Components typically would not override this method, but it can be used to
* cause different tooltips to be displayed differently.
*/
public JToolTip createToolTip() {
JToolTip tip = new JToolTip();
tip.setComponent(this);
return tip;
}
/**
* Forwards the scrollRectToVisible() message to the JComponent's
* parent. Components that can service the request, such as a JViewport,
* override this method and perform the scrolling.
*
* @see JViewport
*/
public void scrollRectToVisible(Rectangle aRect) {
Container parent;
int dx = getX(), dy = getY();
for (parent = getParent();
!(parent == null) &&
!(parent instanceof JComponent) &&
!(parent instanceof CellRendererPane);
parent = parent.getParent()) {
Rectangle bounds = parent.getBounds();
dx += bounds.x;
dy += bounds.y;
}
if (!(parent == null) && !(parent instanceof CellRendererPane)) {
aRect.x += dx;
aRect.y += dy;
((JComponent)parent).scrollRectToVisible(aRect);
aRect.x -= dx;
aRect.y -= dy;
}
}
/**
* If true this component will automatically scroll its contents when
* dragged, if contained in a component that supports scrolling, such as
* JViewport
*
* @see JViewport
* @see #getAutoscrolls
*
* @beaninfo
* expert: true
* description: Whether this component automatically scrolls its contents when dragged.
*/
public void setAutoscrolls(boolean autoscrolls) {
if (autoscrolls) {
if (autoscroller == null) {
autoscroller = new Autoscroller(this);
}
} else {
if (autoscroller != null) {
autoscroller.stop();
autoscroller = null;
}
}
}
/**
* Returns true if this component automatically scrolls its
* contents when dragged, (when contained in a component that supports
* scrolling, like JViewport
*
* @see JViewport
* @see #setAutoscrolls
*/
public boolean getAutoscrolls() {
return autoscroller != null;
}
protected void processMouseMotionEvent(MouseEvent e) {
boolean dispatch = true;
if (autoscroller != null) {
if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
// We don't want to do the drags when the mouse moves if we're
// autoscrolling. It makes it feel spastic.
dispatch = !autoscroller.timer.isRunning();
autoscroller.mouseDragged(e);
}
}
if (dispatch) {
super.processMouseMotionEvent(e);
}
}
// Inner classes can't get at this method from a super class
void superProcessMouseMotionEvent(MouseEvent e) {
super.processMouseMotionEvent(e);
}
static class KeyboardBinding implements Serializable {
ActionListener action;
String command;
KeyStroke keyStroke;
int condition;
KeyboardBinding(ActionListener action,String aCommand,KeyStroke aKeyStroke,int condition) {
this.action = action;
this.command = aCommand;
this.keyStroke = aKeyStroke;
this.condition = condition;
}
ActionListener getAction() {
return action;
}
String getCommand() {
return command;
}
KeyStroke getKeyStroke() {
return keyStroke;
}
int getCondition() {
return condition;
}
public String toString() {
return "KeyBinding ("+action+","+keyStroke+","+condition+")";
}
}
// This class is used by the KeyboardState class to provide a single
// instance which can be stored in the AppContext.
static final class IntVector {
int array[] = null;
int count = 0;
int capacity = 0;
int size() {
return count;
}
int elementAt(int index) {
return array[index];
}
void addElement(int value) {
if (count == capacity) {
capacity = (capacity + 2) * 2;
int[] newarray = new int[capacity];
if (count > 0) {
System.arraycopy(array, 0, newarray, 0, count);
}
array = newarray;
}
array[count++] = value;
}
void setElementAt(int value, int index) {
array[index] = value;
}
}
static class KeyboardState implements Serializable {
private static final Object keyCodesKey =
JComponent.KeyboardState.class;
// Get the array of key codes from the AppContext.
static IntVector getKeyCodeArray() {
IntVector iv =
(IntVector)SwingUtilities.appContextGet(keyCodesKey);
if (iv == null) {
iv = new IntVector();
SwingUtilities.appContextPut(keyCodesKey, iv);
}
return iv;
}
static void registerKeyPressed(int keyCode) {
IntVector kca = getKeyCodeArray();
int count = kca.size();
int i;
for(i=0;i
* The
* If value is null this method will remove the property.
* Changes to client properties are reported with PropertyChange
* events. The name of the property (for the sake of PropertyChange
* events) is
* The clientProperty dictionary is not intended to support large
* scale extensions to JComponent nor should be it considered an
* alternative to subclassing when designing a new component.
*
* @see #getClientProperty
* @see #addPropertyChangeListener
*/
public final void putClientProperty(Object key, Object value) {
Object oldValue = getClientProperties().get(key);
if (value != null) {
getClientProperties().put(key, value);
} else {
getClientProperties().remove(key);
}
firePropertyChange(key.toString(), oldValue, value);
}
/* --- Transitional java.awt.Component Support ---
*
* The methods and fields in this section will migrate to
* java.awt.Component in the next JDK release.
*
*/
private PropertyChangeSupport changeSupport;
/**
* Returns true if this component is a lightweight, i.e. if it doesn't
* have a native window system peer.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @return true if this component is a lightweight
*/
public static boolean isLightweightComponent(Component c) {
return c.getPeer() instanceof java.awt.peer.LightweightPeer;
}
/**
* Moves and resizes this component.
*
* @see java.awt.Component#setBounds
*/
public void reshape(int x, int y, int w, int h) {
if(isShowing()) {
/* If there is an intersection between the new bounds and the old
* one, refresh only the visible rects
*/
if(!((_bounds.x + _bounds.width <= x) ||
(_bounds.y + _bounds.height <= y) ||
(_bounds.x >= (x + w)) ||
(_bounds.y >= (y + h)))) {
Rectangle[] rev = SwingUtilities.computeDifference(getBounds(),
new Rectangle(x,y,w,h));
int i,c;
Container parent = getParent();
for(i=0,c=rev.length ; i < c ; i++) {
parent.repaint(rev[i].x,rev[i].y,rev[i].width,rev[i].height);
// System.out.println("Repaint " + rev[i]);
}
} else {
getParent().repaint(_bounds.x,_bounds.y,_bounds.width,_bounds.height);
}
}
_bounds.setBounds(x, y, w, h);
super.reshape(x, y, w, h);
}
/**
* Moves and resizes this component.
*
* @see java.awt.Component#setBounds
*/
public void setBounds(Rectangle r) {
_bounds.setBounds(r);
super.setBounds(r);
}
/**
* Store the bounds of this component into "return value" rv and
* return rv. If rv is null a new Rectangle is allocated.
* This version of getBounds() is useful if the caller
* wants to avoid allocating a new Rectangle object on the heap.
*
* @param rv the return value, modified to the components bounds
* @return rv
*/
public Rectangle getBounds(Rectangle rv) {
if (rv == null) {
return new Rectangle(getX(), getY(), getWidth(), getHeight());
}
else {
rv.setBounds(getX(), getY(), getWidth(), getHeight());
return rv;
}
}
/**
* Store the width/height of this component into "return value" rv
* and return rv. If rv is null a new Dimension object is
* allocated. This version of getSize() is useful if the
* caller wants to avoid allocating a new Dimension object on the heap.
*
* @param rv the return value, modified to the components size
* @return rv
*/
public Dimension getSize(Dimension rv) {
if (rv == null) {
return new Dimension(getWidth(), getHeight());
}
else {
rv.setSize(getWidth(), getHeight());
return rv;
}
}
/**
* Store the x,y origin of this component into "return value" rv
* and return rv. If rv is null a new Point is allocated.
* This version of getLocation() is useful if the
* caller wants to avoid allocating a new Point object on the heap.
*
* @param rv the return value, modified to the components location
* @return rv
*/
public Point getLocation(Point rv) {
if (rv == null) {
return new Point(getX(), getY());
}
else {
rv.setLocation(getX(), getY());
return rv;
}
}
/**
* Return the current x coordinate of the components origin.
* This method is preferable to writing component.getBounds().x,
* or component.getLocation().x because it doesn't cause any
* heap allocations.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @return the current x coordinate of the components origin.
*/
public int getX() { return _bounds.x; }
/**
* Return the current y coordinate of the components origin.
* This method is preferable to writing component.getBounds().y,
* or component.getLocation().y because it doesn't cause any
* heap allocations.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @return the current y coordinate of the components origin.
*/
public int getY() { return _bounds.y; }
/**
* Return the current width of this component.
* This method is preferable to writing component.getBounds().width,
* or component.getSize().width because it doesn't cause any
* heap allocations.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @return the current width of this component.
*/
public int getWidth() { return _bounds.width; }
/**
* Return the current height of this component.
* This method is preferable to writing component.getBounds().height,
* or component.getSize().height because it doesn't cause any
* heap allocations.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @return the current height of this component.
*/
public int getHeight() { return _bounds.height; }
/**
* Returns true if this Component has the keyboard focus.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @return true if this Component has the keyboard focus.
*/
public boolean hasFocus() {
return getFlag(HAS_FOCUS);
}
/**
* Returns true if this component is completely opaque.
*
* An opaque component paints every pixel within its
* rectangular region. A non-opaque component paints only some of
* its pixels, allowing the pixels underneath it to "show through".
* A component that does not fully paint its pixels therefore
* provides a degree of transparency.
*
* Subclasses that guarantee to always completely paint their contents should
* override this method and return true.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @return true if this component is completely opaque.
* @see #setOpaque
*/
public boolean isOpaque() {
return getFlag(IS_OPAQUE);
}
/**
* If true the components background will be filled with the
* background color. Otherwise, the background is transparent,
* and whatever is underneath will show through.
*
* The default value of this property is false.
*
* This is a JavaBeans bound property.
*
* @see #isOpaque
*/
public void setOpaque(boolean isOpaque) {
boolean oldValue = getFlag(IS_OPAQUE);
setFlag(IS_OPAQUE, isOpaque);
firePropertyChange("opaque", oldValue, isOpaque);
}
/**
* If the specified retangle is completely obscured by any of this
* components opaque children then return true. Only direct children
* are considered, more distant descendants are ignored. A JComponent
* is opaque if JComponent.isOpaque() returns true, other lightweight
* components are always considered transparent, and heavyweight components
* are always considered opaque.
*
* @return true if the specified rectangle is obscured by an opaque child
*/
boolean rectangleIsObscured(int x,int y,int width,int height)
{
Component children[] = getComponents();
if(children == null) {
return false;
}
for(int i = 0; i < children.length; i++) {
Component child = children[i];
Rectangle childBounds;
if (child instanceof JComponent) {
childBounds = ((JComponent)child)._bounds;
} else {
childBounds = child.getBounds();
}
if (x >= childBounds.x && (x + width) <= (childBounds.x + childBounds.width) &&
y >= childBounds.y && (y + height) <= (childBounds.y + childBounds.height)) {
if(child instanceof JComponent) {
return ((JComponent)child).isOpaque();
} else {
/** Sometimes an heavy weight can have a bound larger than it's peer size
* so we should always draw under heavy weights
*/
return false;
}
}
}
return false;
}
/**
* Returns the Component's "visible rect rectangle" - the
* intersection of the visible rectangles for this component
* and all of its ancestors. The return value is stored in
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @param propertyName The programmatic name of the property that was changed.
* @param oldValue The old value of the property.
* @param newValue The new value of the property.
* @see java.beans.PropertyChangeSupport
*/
protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
if (changeSupport != null) {
changeSupport.firePropertyChange(propertyName, oldValue, newValue);
}
}
/*
* PENDING(hmuller) in JDK1.2 the following firePropertyChange overloads
* should additional check for a non-empty listener list with
* changeSupport.hasListeners(propertyName) before calling firePropertyChange.
*/
/**
* @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object)
*/
public void firePropertyChange(String propertyName, byte oldValue, byte newValue) {
if ((changeSupport != null) && (oldValue != newValue)) {
changeSupport.firePropertyChange(propertyName, new Byte(oldValue), new Byte(newValue));
}
}
/**
* @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object)
*/
public void firePropertyChange(String propertyName, char oldValue, char newValue) {
if ((changeSupport != null) && (oldValue != newValue)) {
changeSupport.firePropertyChange(propertyName, new Character(oldValue), new Character(newValue));
}
}
/**
* @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object)
*/
public void firePropertyChange(String propertyName, short oldValue, short newValue) {
if ((changeSupport != null) && (oldValue != newValue)) {
changeSupport.firePropertyChange(propertyName, new Short(oldValue), new Short(newValue));
}
}
/**
* @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object)
*/
public void firePropertyChange(String propertyName, int oldValue, int newValue) {
if ((changeSupport != null) && (oldValue != newValue)) {
changeSupport.firePropertyChange(propertyName, new Integer(oldValue), new Integer(newValue));
}
}
/**
* @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object)
*/
public void firePropertyChange(String propertyName, long oldValue, long newValue) {
if ((changeSupport != null) && (oldValue != newValue)) {
changeSupport.firePropertyChange(propertyName, new Long(oldValue), new Long(newValue));
}
}
/**
* @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object)
*/
public void firePropertyChange(String propertyName, float oldValue, float newValue) {
if ((changeSupport != null) && (oldValue != newValue)) {
changeSupport.firePropertyChange(propertyName, new Float(oldValue), new Float(newValue));
}
}
/**
* @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object)
*/
public void firePropertyChange(String propertyName, double oldValue, double newValue) {
if ((changeSupport != null) && (oldValue != newValue)) {
changeSupport.firePropertyChange(propertyName, new Double(oldValue), new Double(newValue));
}
}
/**
* @see #firePropertyChange(java.lang.String, java.lang.Object, java.lang.Object)
*/
public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) {
if ((changeSupport != null) && (oldValue != newValue)) {
changeSupport.firePropertyChange(propertyName, new Boolean(oldValue), new Boolean(newValue));
}
}
/**
* Add a PropertyChangeListener to the listener list.
* The listener is registered for all properties.
*
* A PropertyChangeEvent will get fired in response to setting
* a bound property, e.g. setFont, setBackground, or setForeground.
* Note that if the current component is inheriting its foreground,
* background, or font from its container, then no event will be
* fired in response to a change in the inherited property.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @param listener The PropertyChangeListener to be added
*/
public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
if (changeSupport == null) {
changeSupport = new java.beans.PropertyChangeSupport(this);
}
changeSupport.addPropertyChangeListener(listener);
}
/**
* Remove a PropertyChangeListener from the listener list.
* This removes a PropertyChangeListener that was registered
* for all properties.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @param listener The PropertyChangeListener to be removed
*/
public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
if (changeSupport != null) {
changeSupport.removePropertyChangeListener(listener);
}
}
/**
* Support for reporting constrained property changes. This method can be called
* when a constrained property has changed and it will send the appropriate
* PropertyChangeEvent to any registered VetoableChangeListeners.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @exception PropertyVetoException when the attempt to set the property is vetoed
* by the receiver.
*/
protected void fireVetoableChange(String propertyName, Object oldValue, Object newValue)
throws java.beans.PropertyVetoException
{
if (vetoableChangeSupport == null) {
return;
}
vetoableChangeSupport.fireVetoableChange(propertyName, oldValue, newValue);
}
/**
* Add a VetoableChangeListener to the listener list.
* The listener is registered for all properties.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @param listener The VetoableChangeListener to be added
*/
public synchronized void addVetoableChangeListener(VetoableChangeListener listener) {
if (vetoableChangeSupport == null) {
vetoableChangeSupport = new java.beans.VetoableChangeSupport(this);
}
vetoableChangeSupport.addVetoableChangeListener(listener);
}
/**
* Remove a VetoableChangeListener from the listener list.
* This removes a VetoableChangeListener that was registered
* for all properties.
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @param listener The VetoableChangeListener to be removed
*/
public synchronized void removeVetoableChangeListener(VetoableChangeListener listener) {
if (vetoableChangeSupport == null) {
return;
}
vetoableChangeSupport.removeVetoableChangeListener(listener);
}
/**
* Returns the top-level ancestor of this component (either the
* containing Window or Applet), or null if this component has not
* been added to any container.
*
* @return the top-level Container which this component is in.
*/
public Container getTopLevelAncestor() {
for(Container p = this; p != null; p = p.getParent()) {
if(p instanceof Window || p instanceof Applet) {
return p;
}
}
return null;
}
/**
* Registers listener so that it will receive AncestorEvents
* when it or any of its ancestors move or are made visible / invisible.
* Events are also sent when the component or its ancestors are added
* or removed from the Component hierarchy
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @see AncestorEvent
*/
public void addAncestorListener(AncestorListener listener) {
if (ancestorNotifier == null) {
ancestorNotifier = new AncestorNotifier(this);
}
ancestorNotifier.addAncestorListener(listener);
}
/**
* Unregisters listener so that it will no longer receive
* AncestorEvents
*
* This method will migrate to java.awt.Component in the next major JDK release
*
* @see #addAncestorListener
*/
public void removeAncestorListener(AncestorListener listener) {
if (ancestorNotifier == null) {
return;
}
ancestorNotifier.removeAncestorListener(listener);
if (ancestorNotifier.listenerList.getListenerList().length == 0) {
ancestorNotifier.removeAllListeners();
ancestorNotifier = null;
}
}
/**
* Notification to this component that it now has a parent component.
* When this method is invoked, the chain of parent components is
* set up with KeyboardAction event listeners.
*
* @see #registerKeyboardAction
*/
public void addNotify() {
super.addNotify();
firePropertyChange("ancestor", null, getParent());
}
/**
* Notification to this component that it no longer has a parent component.
* When this method is invoked, any KeyboardActions set up in the
* the chain of parent components are removed.
*
* @see #registerKeyboardAction
*/
public void removeNotify() {
super.removeNotify();
// This isn't strictly correct. The event shouldn't be
// fired until *after* the parent is set to null. But
// we only get notified before that happens
firePropertyChange("ancestor", getParent(), null);
}
/**
* Adds the specified region to the dirty region list if the component
* is showing. The component will be repainted after all of the
* currently pending events have been dispatched.
*
* @see java.awt.Component#isShowing
* @see RepaintManager#addDirtyRegion
*/
public void repaint(long tm, int x, int y, int width, int height) {
RepaintManager.currentManager(this).addDirtyRegion(this, x, y, width, height);
}
/**
* Adds the specified region to the dirty region list if the component
* is showing. The component will be repainted after all of the
* currently pending events have been dispatched.
*
* @see java.awt.Component#isShowing
* @see RepaintManager#addDirtyRegion
*/
public void repaint(Rectangle r) {
repaint(0,r.x,r.y,r.width,r.height);
}
/**
* Support for deferred automatic layout.
*
* Calls invalidate() and then adds this components validateRoot
* to a list of components that need to be validated. Validation
* will occur after all currently pending events have been dispatched.
* By default only JScrollPane.isValidateRoot() returns true. In other
* words after this method is called, the first JScrollPane that
* contains this component (if any) will be validated.
*
* @see java.awt.Component#invalidate
* @see java.awt.Container#validate
* @see #isValidateRoot
* @see RepaintManager#addInvalidComponent
*/
public void revalidate() {
if (SwingUtilities.isEventDispatchThread()) {
invalidate();
RepaintManager.currentManager(this).addInvalidComponent(this);
}
else {
Runnable callRevalidate = new Runnable() {
public void run() {
revalidate();
}
};
SwingUtilities.invokeLater(callRevalidate);
}
}
/**
* If this method returns true, revalidate() calls by descendants of
* this component will cause the entire tree beginning with this root
* to be validated. Returns false by default. JScrollPane overrides
* this method and returns true.
*
* @return false
* @see #revalidate
* @see java.awt.Component#invalidate
* @see java.awt.Container#validate
*/
public boolean isValidateRoot() {
return false;
}
/**
* Returns true if this component tiles its children, i.e. if
* it can guarantee that the children will not overlap. The
* repainting system is substantially more efficient in this
* common case. JComponent subclasses that can't make this
* guarantee, e.g. JLayeredPane, should override this method
* to return false.
*
* @return true if this components children don't overlap
*/
public boolean isOptimizedDrawingEnabled() {
return true;
}
/**
* Paint the specified region in this component and all of its
* descendants that overlap the region, immediately.
*
* It's rarely neccessary to call this method. In most cases it's
* more efficient to call repaint which defers the actual painting
* and can collapse redundant requests into a single paint call.
* This method is useful if one needs to update the display while
* the current event is being dispatched.
*
* @see #repaint
*/
public void paintImmediately(int x,int y,int w, int h) {
Component c = this;
Component parent;
Rectangle bounds;
if(!isShowing()) {
return;
}
while(!((JComponent)c).isOpaque()) {
parent = c.getParent();
if(parent != null) {
if(c instanceof JComponent) {
bounds = ((JComponent)c)._bounds;
} else {
bounds = c.getBounds();
}
x += bounds.x;
y += bounds.y;
c = parent;
} else {
break;
}
if(!(c instanceof JComponent)) {
break;
}
}
if(c instanceof JComponent) {
((JComponent)c)._paintImmediately(x,y,w,h);
} else {
c.repaint(x,y,w,h);
}
}
/**
* Paint the specified region now.
*
* This method will migrate to java.awt.Component in the next major JDK release
*/
public void paintImmediately(Rectangle r) {
paintImmediately(r.x,r.y,r.width,r.height);
}
void _paintImmediately(int x, int y, int w, int h) {
Graphics g;
Container c;
Rectangle clip = new Rectangle(x,y,w,h);
Rectangle b;
if(tmpRect == null) {
tmpRect = new Rectangle();
}
tmpRect.x=tmpRect.y=0;
int offsetX=0,offsetY=0;
int bufferOffsetX=0,bufferOffsetY=0;
boolean hasBuffer = false;
JComponent bufferedComponent = null;
JComponent paintingComponent = this;
RepaintManager repaintManager = RepaintManager.currentManager(this);
for (c = this;
c != null && !(c instanceof Window) && !(c instanceof Applet);
c = c.getParent()) {
if ((c instanceof JComponent) && !(((JComponent)c).isOptimizedDrawingEnabled())) {
paintingComponent = (JComponent)c;
offsetX = offsetY = 0;
hasBuffer = false; /** Get rid of any buffer since we draw from here and
* we might draw something larger
*/
}
if(repaintManager.isDoubleBufferingEnabled() &&
(c instanceof JComponent) && ((JComponent)c).isDoubleBuffered()) {
hasBuffer = true;
bufferedComponent = (JComponent)c;
bufferOffsetX = offsetX;
bufferOffsetY = offsetY;
}
if(c instanceof JComponent) {
b = ((JComponent)c)._bounds;
} else {
b = c.getBounds();
}
tmpRect.width = b.width;
tmpRect.height = b.height;
SwingUtilities.computeIntersection(tmpRect.x,tmpRect.y,tmpRect.width,tmpRect.height,clip);
clip.x += b.x;
clip.y += b.y;
offsetX += b.x;
offsetY += b.y;
}
if(c == null || c.getPeer() == null) {
return;
}
clip.x -= offsetX;
clip.y -= offsetY;
try {
g = SwingGraphics.createSwingGraphics(paintingComponent.getGraphics());
} catch(NullPointerException e) {
g = null;
e.printStackTrace();
}
if(g == null) {
System.err.println("In paintImmediately null graphics");
return;
}
if(hasBuffer) {
Image offscreen = repaintManager.getOffscreenBuffer(bufferedComponent,clip.width,clip.height);
paintWithBuffer(paintingComponent,g,clip,offscreen);
g.dispose();
} else {
g.setClip(clip.x,clip.y,clip.width,clip.height);
try {
paintingComponent.paint(g);
} finally {
g.dispose();
}
}
}
private void paintWithBuffer(JComponent paintingComponent,Graphics g,Rectangle clip,Image offscreen) {
Graphics og = SwingGraphics.createSwingGraphics(offscreen.getGraphics());
int bw = offscreen.getWidth(null);
int bh = offscreen.getHeight(null);
int x,y,maxx,maxy;
if(bw > clip.width) {
bw = clip.width;
}
if(bh > clip.height) {
bh = clip.height;
}
try {
paintingComponent.setFlag(ANCESTOR_USING_BUFFER,true);
paintingComponent.setFlag(IS_PAINTING_TILE,true);
for(x = clip.x, maxx = clip.x+clip.width;
x < maxx ; x += bw ) {
for(y=clip.y, maxy = clip.y + clip.height;
y < maxy ; y += bh) {
if((y+bh) >= maxy && (x+bw) >= maxx) {
paintingComponent.setFlag(IS_PAINTING_TILE,false);
}
og.translate(-x,-y);
og.setClip(x,y,bw,bh);
paintingComponent.paint(og);
g.setClip(x,y,bw,bh);
g.drawImage(offscreen,x,y,paintingComponent);
og.translate(x,y);
}
}
} finally {
paintingComponent.setFlag(ANCESTOR_USING_BUFFER,false);
paintingComponent.setFlag(IS_PAINTING_TILE,false);
og.dispose();
}
}
private void setFlag(int aFlag, boolean aValue) {
if(aValue) {
flags |= (1 << aFlag);
} else {
flags &= ~(1 << aFlag);
}
}
private boolean getFlag(int aFlag) {
int mask = (1 << aFlag);
return ((flags & mask) == mask);
}
/** Buffering **/
/** Set whether the receiving component should use a buffer to paint.
* If set to true, all the drawing from this component will be done
* in an offscreen painting buffer. The offscreen painting buffer will
* the be copied onto the screen.
* Swing's painting system always use a maximum of one double buffer.
* If a Component is buffered and one of its ancestor is also buffered,
* the ancestor buffer will be used.
*/
public void setDoubleBuffered(boolean aFlag) {
setFlag(IS_DOUBLE_BUFFERED,aFlag);
}
/** Return whether the receiving component should use a buffer to paint. **/
public boolean isDoubleBuffered() {
return getFlag(IS_DOUBLE_BUFFERED);
}
/**
* Returns the JRootPane ancestor for a component
*
* @return the JRootPane that contains this component,
* or null if no JRootPane is found
*/
public JRootPane getRootPane() {
return SwingUtilities.getRootPane(this);
}
/** Serialization **/
/*
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
}
private void readObject(ObjectInputStream s)
throws IOException, ClassNotFoundException
{
s.defaultReadObject();
updateUI();
}
*/
}
isOpaque
.)
*
* @param c the new Color for the background
* @see JComponent#isOpaque
*/
public void setBackground(Color c) {
JComponent.this.setBackground(c);
}
/**
* Get the foreground color of this object.
*
* @return the foreground color, if supported, of the object;
* otherwise, null
*/
public Color getForeground() {
return JComponent.this.getForeground();
}
/**
* Set the foreground color of this object.
*
* @param c the new Color for the foreground
*/
public void setForeground(Color c) {
JComponent.this.setForeground(c);
}
/**
* Get the Cursor of this object.
*
* @return the Cursor, if supported, of the object; otherwise, null
*/
public Cursor getCursor() {
return JComponent.this.getCursor();
}
/**
* Set the Cursor of this object.
*
* @param c the new Cursor for the object
*/
public void setCursor(Cursor cursor) {
JComponent.this.setCursor(cursor);
}
/**
* Get the Font of this object.
*
* @return the Font,if supported, for the object; otherwise, null
*/
public Font getFont() {
return JComponent.this.getFont();
}
/**
* Set the Font of this object.
*
* @param f the new Font for the object
*/
public void setFont(Font f) {
JComponent.this.setFont(f);
}
/**
* Get the FontMetrics of this object.
*
* @param f the Font
* @return the FontMetrics, if supported, the object; otherwise, null
* @see #getFont
*/
public FontMetrics getFontMetrics(Font f) {
return JComponent.this.getFontMetrics(f);
}
/**
* Determine if the object is enabled.
*
* @return true if object is enabled; otherwise, false
*/
public boolean isEnabled() {
return JComponent.this.isEnabled();
}
/**
* Set the enabled state of the object.
*
* @param b if true, enables this object; otherwise, disables it
*/
public void setEnabled(boolean b) {
boolean old = JComponent.this.isEnabled();
JComponent.this.setEnabled(b);
if (b != old) {
if (accessibleContext != null) {
if (b) {
accessibleContext.firePropertyChange(
AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
null, AccessibleState.ENABLED);
} else {
accessibleContext.firePropertyChange(
AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
AccessibleState.ENABLED, null);
}
}
}
}
/**
* Determine if the object is visible. Note: this means that the
* object intends to be visible; however, it may not in fact be
* showing on the screen because one of the objects that this object
* is contained by is not visible. To determine if an object is
* showing on the screen, use isShowing().
*
* @return true if object is visible; otherwise, false
*/
public boolean isVisible() {
return JComponent.this.isVisible();
}
/**
* Set the visible state of the object.
*
* @param b if true, shows this object; otherwise, hides it
*/
public void setVisible(boolean b) {
boolean old = JComponent.this.isVisible();
JComponent.this.setVisible(b);
if (b != old) {
if (accessibleContext != null) {
if (b) {
accessibleContext.firePropertyChange(
AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
null, AccessibleState.VISIBLE);
} else {
accessibleContext.firePropertyChange(
AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
AccessibleState.VISIBLE, null);
}
}
}
}
/**
* Determine if the object is showing. This is determined by checking
* the visibility of the object and ancestors of the object. Note:
* this will return true even if the object is obscured by another
* (for example, it happens to be underneath a menu that was pulled
* down).
*
* @return true if object is showing; otherwise, false
*/
public boolean isShowing() {
return JComponent.this.isShowing();
}
/**
* Checks whether the specified point is within this object's bounds,
* where the point's x and y coordinates are defined to be relative to
* the coordinate system of the object.
*
* @param p the Point relative to the coordinate system of the object
* @return true if object contains Point; otherwise false
*/
public boolean contains(Point p) {
return JComponent.this.contains(p);
}
/**
* Returns the location of the object on the screen.
*
* @return location of object on screen -- can be null if this object
* is not on the screen
*/
public Point getLocationOnScreen() {
if (JComponent.this.isShowing()) {
return JComponent.this.getLocationOnScreen();
} else {
return null;
}
}
/**
* Gets the location of the object relative to the parent in the form
* of a point specifying the object's top-left corner in the screen's
* coordinate space.
*
* @return An instance of Point representing the top-left corner of
* the objects's bounds in the coordinate space of the screen; null if
* this object or its parent are not on the screen
*/
public Point getLocation() {
return JComponent.this.getLocation();
}
/**
* Sets the location of the object relative to the parent.
*/
public void setLocation(Point p) {
JComponent.this.setLocation(p);
}
/**
* Gets the bounds of this object in the form of a Rectangle object.
* The bounds specify this object's width, height, and location
* relative to its parent.
*
* @return A rectangle indicating this component's bounds; null if
* this object is not on the screen.
*/
public Rectangle getBounds() {
return JComponent.this.getBounds();
}
/**
* Sets the bounds of this object in the form of a Rectangle object.
* The bounds specify this object's width, height, and location
* relative to its parent.
*
* @param A rectangle indicating this component's bounds
*/
public void setBounds(Rectangle r) {
JComponent.this.setBounds(r);
}
/**
* Returns the size of this object in the form of a Dimension object.
* The height field of the Dimension object contains this objects's
* height, and the width field of the Dimension object contains this
* object's width.
*
* @return A Dimension object that indicates the size of this
* component; null if this object is not on the screen
*/
public Dimension getSize() {
return JComponent.this.getSize();
}
/**
* Resizes this object so that it has width width and height.
*
* @param d - The dimension specifying the new size of the object.
*/
public void setSize(Dimension d) {
JComponent.this.setSize(d);
}
/**
* Returns the Accessible child, if one exists, contained at the local
* coordinate Point.
*
* @param p The point defining the top-left corner of the Accessible,
* given in the coordinate space of the object's parent.
* @return the Accessible, if it exists, at the specified location;
* else null
*/
public Accessible getAccessibleAt(Point p) {
return SwingUtilities.getAccessibleAt(JComponent.this, p);
}
/**
* Returns whether this object can accept focus or not.
*
* @return true if object can accept focus; otherwise false
*/
public boolean isFocusTraversable() {
return JComponent.this.isFocusTraversable();
}
/**
* Requests focus for this object.
*/
public void requestFocus() {
JComponent.this.requestFocus();
}
/**
* Adds the specified focus listener to receive focus events from this
* component.
*
* @param l the focus listener
*/
public void addFocusListener(FocusListener l) {
JComponent.this.addFocusListener(l);
}
/**
* Removes the specified focus listener so it no longer receives focus
* events from this component.
*
* @param l the focus listener
*/
public void removeFocusListener(FocusListener l) {
JComponent.this.removeFocusListener(l);
}
} // inner class AccessibleJComponent
/**
* @return a small Hashtable
* @see #putClientProperty
* @see #getClientProperty
*/
private Dictionary getClientProperties() {
if (clientProperties == null) {
clientProperties = new Hashtable(2);
}
return clientProperties;
}
/**
* Returns the value of the property with the specified key. Only
* properties added with putClientProperty
will return
* a non-null value.
*
* @return the value of this property or null
* @see #putClientProperty
*/
public final Object getClientProperty(Object key) {
return getClientProperties().get(key);
}
/**
* Add an arbitrary key/value "client property" to this component.
* get/putClientProperty
methods provide access to
* a small per-instance hashtable. Callers can use get/putClientProperty
* to annotate components that were created by another module, e.g. a
* layout manager might store per child constraints this way. For example:
*
* componentA.putClientProperty("to the left of", componentB);
*
* key.toString()
.
* visibleRect
*
* @see #getVisibleRect
*/
static final void computeVisibleRect(Component c, Rectangle visibleRect) {
Container p = c.getParent();
Rectangle bounds = c.getBounds();
if (p == null || p instanceof Window || p instanceof Applet) {
visibleRect.setBounds(0, 0, bounds.width, bounds.height);
} else {
computeVisibleRect(p, visibleRect);
visibleRect.x -= bounds.x;
visibleRect.y -= bounds.y;
SwingUtilities.computeIntersection(0,0,bounds.width,bounds.height,visibleRect);
}
}
/**
* Returns the Component's "visible rect rectangle" - the
* intersection of the visible rectangles for this component
* and all of its ancestors. The return value is stored in
* visibleRect
*
* @see #getVisibleRect
*/
public void computeVisibleRect(Rectangle visibleRect) {
computeVisibleRect(this, visibleRect);
}
/**
* Returns the Component's "visible rectangle" - the
* intersection of this components visible rectangle:
*
* new Rectangle(0, 0, getWidth(), getHeight());
*
* and all of its ancestors visible Rectangles.
*
* @return the visible rectangle
*/
public Rectangle getVisibleRect() {
Rectangle visibleRect = new Rectangle();
computeVisibleRect(visibleRect);
return visibleRect;
}
/**
* Support for reporting bound property changes. If oldValue and
* newValue are not equal and the PropertyChangeEvent listener list
* isn't empty, then fire a PropertyChange event to each listener.
* This method has an overloaded method for each primitive type. For
* example, here's how to write a bound property set method whose
* value is an int:
*
* public void setFoo(int newValue) {
* int oldValue = foo;
* foo = newValue;
* firePropertyChange("foo", oldValue, newValue);
* }
*
*